package net.web.belerofont.controller;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.web.belerofont.action.Action;
import net.web.belerofont.action.URIMap;
import net.web.belerofont.action.support.InvokerContextSupport;
import net.web.belerofont.annotation.Inject;

import org.apache.log4j.Logger;

public class Controller  implements Filter {
    private ConfigReader configReader; 
    private ActionFlow actionFlow;
    private InterceptFlow interceptFlow;
    
	ServletContext context;

	public final Logger LOG = Logger.getLogger(Controller.class);
	
	private void inject(Action action, HttpServletRequest request, HttpServletResponse response) {
		try {
			Method[] methods = action.getClass().getDeclaredMethods();
			for(Method method: methods) {
				Annotation[] annotations = method.getAnnotations();
				for(Annotation annotation:annotations) {
					if (annotation instanceof Inject && ((Inject)annotation).direction().equals("IN")) {
						Class params[] = method.getParameterTypes();
						if (params.length == 1) {
							if (params[0].getName().equalsIgnoreCase("javax.servlet.ServletContext")) {
									method.invoke(action, new Object[]{context});
								
							}
							if (params[0].getName().equalsIgnoreCase("javax.servlet.http.HttpServletRequest")) {
								method.invoke(action, new Object[]{request});
							
							}
							if (params[0].getName().equalsIgnoreCase("javax.servlet.http.HttpServletResponse")) {
								method.invoke(action, new Object[]{response});
							
							}
						}
					}
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
    
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
			FilterChain chain) throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest)servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		
		String uri = request.getRequestURI();

		Action action = actionFlow.getAction(uri);

		
		String uriTransfer;
		
		if (action == null) 
			chain.doFilter(servletRequest, servletResponse);
		else {
			inject(action, request, response);
			
			URIMap uriMap = interceptFlow.invoke(new InvokerContextSupport(action,request,response));

			if (uriMap != null) {
				uriTransfer = uriMap.getUri();
				if (uriMap.getTransferType().equals(URIMap.TransferType.REDIRECT)) {
					response.sendRedirect(uriTransfer);
				}
				else {
					if (uriMap.getTransferType().equals(URIMap.TransferType.FORWARD)){
						RequestDispatcher view = request.getRequestDispatcher(uriTransfer);
						view.forward(request, response);
					} else {
						throw new ServletException("Invalid flow declararion in the belerofonte config file");
					}
				}
			}
		}
	}
	 
	public void init(FilterConfig config) {
		
		try {
			this.context = config.getServletContext();

			String configFile = config.getInitParameter("CONFIG-FILE");
			configFile = config.getServletContext().getRealPath(configFile);
			LOG.debug("Config file: " + configFile);

			this.configReader = new ConfigAnotationReader(new ConfigXMLReader(configFile));
			
			this.actionFlow = ActionFlow.instance(configReader);
			
			this.interceptFlow = InterceptFlow.instance(configReader);
		} catch (CreateConfigException e) {
			LOG.error("Init Error", e);
		}
		
	}

	public void destroy() {
		
	}
	 
}
 
